import { LanguageServerCommandTypes } from "./types";

export enum TaskQueueingType {
    // 一般顺序，添加时会排序到 lowPriority 之前
    normal,
    // 低优先度，在 normal 后执行
    lowPriority,
    // 固定顺序，不重排
    fence,
}

export interface TaskCallback {
    (): any;
}

export interface TaskAsyncCallback {
    (): Promise<any>;
}

export interface Task {
    taskType: keyof LanguageServerCommandTypes;
    sequence: number;
    expectResponse: boolean;
    isAsync: boolean;
    queueingType: TaskQueueingType;
    callback: TaskCallback | TaskAsyncCallback;
}

export class TaskQueue {
    private readonly _queue: Task[];
    private _sequence;

    public constructor() {
        this._queue = [];
        this._sequence = 0;
    }

    public get length() {
        return this._queue.length;
    }

    public enqueue(task: Task): void {
        if (task.queueingType === TaskQueueingType.normal) {
            let index = this._queue.length - 1;
            while (index >= 0) {
                if (this._queue[index].queueingType !== TaskQueueingType.lowPriority) {
                    break;
                }
                index--;
            }
            this._queue.splice(index + 1, 0, task);
        }
        else {
            this._queue.push(task);
        }
    }

    public dequeue(): Task | undefined {
        return this._queue.shift();
    }

    public createTask<K extends keyof LanguageServerCommandTypes>(command: K, queueingType: TaskQueueingType, expectResponse: boolean, callback: () => void): Task {
        return {
            taskType: command,
            sequence: this._sequence++,
            expectResponse,
            queueingType,
            isAsync: false,
            callback,
        };
    }

    public tryDeletePendingTask(sequence: number): boolean {
        for (let i = 0; i < this._queue.length; i++) {
            if (this._queue[i].sequence === sequence) {
                this._queue.splice(i, 1);
                return true;
            }
        }
        return false;
    }

}

export class TaskMap {

    private _tasks: Map<number, Task>;
    private _asyncTasks: Map<number, Task>;

    public constructor() {
        this._tasks = new Map();
        this._asyncTasks = new Map();
    }

    private delete(sequence: number) {
        if (!this._tasks.delete(sequence)) {
            this._asyncTasks.delete(sequence);
        }
    }

    public fetch(sequence: number): Task | undefined {
        let callback = this._tasks.get(sequence) || this._asyncTasks.get(sequence);
        this.delete(sequence);
        return callback;
    }

    public add(task: Task) {
        if (task.isAsync) {
            this._asyncTasks.set(task.sequence, task);
        }
        else {
            this._tasks.set(task.sequence, task);
        }
    }

    public dispose() {
        this._tasks.clear();
        this._asyncTasks.clear();
    }

}
